package com.xzzz.irda.guardcat.server.metric;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import com.xzzz.irda.guardcat.core.constants.GuardcatConstant;
import com.xzzz.irda.guardcat.server.app.pojo.MachineInfo;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 指标存储
 *
 * @param <M> 具体的指标对象
 * @author xzzz
 */
@Slf4j
@SuppressWarnings("ALL")
public abstract class MetricRepository<M> {

    /**
     * 指标监听方法, (监听类：监听类下的监听方法)
     */
    private static final Map<String, List<Method>> METRIC_LISTENER_METHOD = new HashMap<>();

    /**
     * 监听方法所在的对象引用, (监控方法：方法所在的对象引用)
     */
    private static final Map<Method, Object> METRIC_LISTENER_METHOD_INSTANCE = new HashMap<>();

    /**
     * 初始化相关数据
     */
    static {
        Set<Class<?>> metricListenerClazzList = ClassUtil.scanPackage(GuardcatConstant.SERVER_PACKAGE);
        bindMetricListener(metricListenerClazzList);
    }

    /**
     * 获取目录下被 {@link MetricListener} 注解的方法, 该方法会与注解中的 {@link MetricListener#metric()} 参数进行绑定, 当
     * 获取到 {@link MetricListener#metric()} 对应的指标对象时, 会调用被注解的方法, 方法中可以执行自定义的监听逻辑, 如最近1分钟
     * 的内存大小, 记录历史以来的内存大小等等
     *
     * @param metricListenerClazzList
     */
    private static void bindMetricListener(Set<Class<?>> metricListenerClazzList) {
        try {
            for (Class<?> listenerClazz : metricListenerClazzList) {
                for (Method listenerMethod : listenerClazz.getMethods()) {
                    MetricListener metricListener = AnnotationUtil.getAnnotation(listenerMethod, MetricListener.class);
                    if (metricListener == null) {
                        continue;
                    }
                    // 检查被注解的方法的参数是否合法
                    Class<?>[] classes = listenerMethod.getParameterTypes();
                    if (classes.length != 2) {
                        log.warn("{} 监听类方法 {} 参数个数错误", listenerClazz.getSimpleName(), listenerMethod.getName());
                        continue;
                    }

                    if (!ClassUtil.equals(classes[0], MachineInfo.class.getName(), true)) {
                        log.warn("{}.{} 的第一个参数必须为 {}", listenerClazz.getSimpleName(), listenerMethod.getName(), MachineInfo.class.getName());
                    }

                    if (!ClassUtil.equals(classes[1], metricListener.metric().getName(), true)) {
                        log.warn("{}.{} 的第二个参数必须为 {}", listenerClazz.getSimpleName(), listenerMethod.getName(), metricListener.metric().getName());
                    }

                    try {
                        if (null == METRIC_LISTENER_METHOD_INSTANCE.get(listenerMethod)) {
                            METRIC_LISTENER_METHOD_INSTANCE.put(listenerMethod, listenerClazz.newInstance());
                        }

                        List<Method> methods;
                        if ((methods = METRIC_LISTENER_METHOD.get(metricListener.metric().getName())) == null) {
                            methods = new ArrayList<>();
                            METRIC_LISTENER_METHOD.put(metricListener.metric().getName(), methods);
                        }
                        methods.add(listenerMethod);
                    } catch (InstantiationException | IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    protected String buildUrl(MachineInfo machine, String path) {
        return "http://" + machine.getIp() + ":" + machine.getPort() + machine.getServletContextPath() + path;
    }

    /**
     * 收到指标时发布事件
     *
     * @param machineInfo 指标所属的机器信息
     * @param m           指标信息
     */
    public final void publish(MachineInfo machineInfo, M m) {
        List<Method> methods = METRIC_LISTENER_METHOD.get(m.getClass().getName());
        if (CollUtil.isEmpty(methods)) {
            return;
        }
        for (Method method : methods) {
            try {
                method.invoke(METRIC_LISTENER_METHOD_INSTANCE.get(method), machineInfo, m);
            } catch (IllegalAccessException | InvocationTargetException e) {
                log.warn("调用 {}.{} 方法失败:{}", method.getDeclaringClass().getSimpleName(), method.getName(), e.getMessage());
            }
        }
    }

    /**
     * 保存数据
     *
     * @param machineInfo 机器信息
     * @param m           指标信息
     */
    public abstract void save(MachineInfo machineInfo, M m);

    /**
     * 淘汰数据
     */
    public abstract void expire();

    /**
     * 根据 machineId 查询数据
     *
     * @param machineId 机器的 id
     * @return 指标信息
     */
    public abstract M getByMachineId(String machineId);

    /**
     * 根据 machineId 查询数据集合
     *
     * @param machineId 机器的 id
     * @return 指标集合
     */
    public abstract List<M> listByMachineId(String machineId);

    /**
     * 查询集合
     *
     * @param machineInfo 机器信息
     * @param m           指标条件
     * @return 指标集合
     */
    public List<M> list(MachineInfo machineInfo, M m, Map<String, Object> args) {
        return null;
    }
}
